package com.sentrix.auto.printercenter.application;

import com.google.gson.Gson;
import com.sentrix.auto.printercenter.common.AssertUtil;
import com.sentrix.auto.printercenter.domain.entity.PrinterEntity;
import com.sentrix.auto.printercenter.domain.entity.PrinterTaskEntity;
import com.sentrix.auto.printercenter.infrastructure.agentserver.AgentClientManager;
import com.sentrix.auto.printercenter.infrastructure.agentserver.dto.BaseWebSocketResMsg;
import com.sentrix.auto.printercenter.infrastructure.persistence.IPrinterRepository;
import com.sentrix.auto.printercenter.presentation.api.dto.PrintReqDto;
import com.sentrix.auto.printercenter.infrastructure.persistence.IPrintTaskRepository;
import com.sentrix.auto.printercenter.infrastructure.agentserver.dto.CallMethodType;
import com.sentrix.auto.printercenter.infrastructure.agentserver.dto.PrintParam;
import com.sentrix.auto.printercenter.infrastructure.agentserver.dto.BaseQZPrintReq;
import com.sentrix.auto.printercenter.infrastructure.agentserver.WebSocketResSyncHandler;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.sentrix.auto.printercenter.presentation.api.dto.ScanPrintReqDto;
import com.sentrix.auto.printercenter.presentation.center.dto.PrintSimpleReqDto;
import com.sentrix.auto.printercenter.presentation.center.dto.PrinterResDto;
import jakarta.annotation.Resource;
import jakarta.validation.constraints.NotNull;
import lombok.extern.slf4j.Slf4j;
import org.modelmapper.ModelMapper;
import org.modelmapper.TypeToken;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Objects;
import java.util.function.Function;
import java.util.stream.Collectors;

@Slf4j
@Service
public class PrinterService {
    private final ObjectMapper objectMapper = new ObjectMapper();
    private final ModelMapper modelMapper = new ModelMapper();
    private final WebSocketResSyncHandler syncHandler = WebSocketResSyncHandler.instance();
    private final AgentClientManager printClientManager = AgentClientManager.instance();
    private final Gson gson = new Gson();

    @Resource
    private IPrintTaskRepository printingTaskRepository;
    @Resource
    private IPrinterRepository printerRepository;

    /**
     * 打印
     *
     * @param printRequestDto
     */
    public void print(PrintReqDto printRequestDto) {
        //根据打印机Id查询打印机
        Long printerCode = printRequestDto.getPrinterCode();
        //校验打印机是否有效
        boolean isValidPrinter;
        //获取打印机名称和agent mac
        String printerName = "";
        String agentMac = "";
        String jobName = "";
        if (!printClientManager.isClientOnline(agentMac)) {
            throw new RuntimeException("printer agent is offline!");
        }

        BaseQZPrintReq<PrintParam> qzPrintReqParam = buildPrintParam(agentMac, printerName, jobName, printRequestDto);
        //保存打印job到数据库
//        printingTaskDb.save();


    }

    /**
     * 扫码打印
     *
     * @param scanPrintReqDto
     */
    public void scanPrint(ScanPrintReqDto scanPrintReqDto) {
        //检查打印机，及状态
        PrinterEntity printer = printerRepository.findByCode(scanPrintReqDto.getPrinterCode());
        AssertUtil.assertNotNull(printer, "no printer for printerCode:" + scanPrintReqDto.getPrinterCode());
        if (!printer.getIsOnline()) {
            throw new RuntimeException("printer is offline,please check!");
        }
        //2、生成打印task
        PrinterTaskEntity task = new PrinterTaskEntity();
        task.setOrderNo(scanPrintReqDto.getOrderNo());
        task.setPrinterCode(scanPrintReqDto.getPrinterCode());
        task.setAgentCode(printer.getAgentCode());
        task.setAgentName(printer.getAgentName());
        task.setTaskStatus(PrinterTaskEntity.TASKSTATUS.WAITING);
        task.setData(gson.toJson(scanPrintReqDto));

        if (!printingTaskRepository.save(task)) {
            throw new RuntimeException("save print task failed!");
        }

        //3、构建打印参数
        PrintParam printParam = new PrintParam(printer.getName(), scanPrintReqDto.getOrderNo());
        printParam.addData(scanPrintReqDto.getFileType(), scanPrintReqDto.getFlavor(), scanPrintReqDto.getFileData());
        BaseQZPrintReq<PrintParam> printReq = new BaseQZPrintReq<>();
        printReq.setParams(printParam);
        printReq.setCall(CallMethodType.PRINT);
        BaseWebSocketResMsg resMsg = printClientManager.sendMessage(printer.getAgentCode(), printReq, 30);
        if (!resMsg.isSuccess()) {
            log.error("打印失败：{}", resMsg.getResult());
            task.setErrorMessage(resMsg.getResult());
            printingTaskRepository.updateById(task);
        }
    }

    public void print(PrintSimpleReqDto simpleReqDto) {
        PrinterEntity printer = printerRepository.findByCode(simpleReqDto.getPrinterCode());
        PrintParam printParam = new PrintParam(printer.getName(), simpleReqDto.getOrderCode());
        printParam.addData(simpleReqDto.getDataType(), simpleReqDto.getDataEncodeFormat(), simpleReqDto.getData());

        BaseQZPrintReq<PrintParam> printReq = new BaseQZPrintReq<>();
        printReq.setParams(printParam);
        printReq.setCall(CallMethodType.PRINT);
        BaseWebSocketResMsg resMsg = printClientManager.sendMessage(printer.getAgentCode(), printReq, 10);
        if (!resMsg.isSuccess()) {
            log.error("打印失败：{}", resMsg.getResult());

        }
    }

    private BaseQZPrintReq<PrintParam> buildPrintParam(String agentMac, String printerName, String jobName, PrintReqDto printRequestDto) {
        BaseQZPrintReq<PrintParam> printParam = new BaseQZPrintReq<>();
        printParam.setCall(CallMethodType.PRINT);
        PrintParam subPrintParam = new PrintParam(printerName, jobName);
        printParam.setParams(subPrintParam);


        subPrintParam.setOptions(modelMapper.map(printRequestDto.getOptions(), PrintParam.Option.class));
        subPrintParam.setData(modelMapper.map(printRequestDto.getData(), new TypeToken<List<PrintParam.PrintData>>() {
        }.getType()));
        return printParam;
    }

    public <R> List<R> findPrinterByAgent(String mac, Function<PrinterEntity, R> fromDomain) {
        List<PrinterEntity> printerEntities = printerRepository.findByAgentCode(mac);
        return printerEntities.stream().map(fromDomain).collect(Collectors.toList());
    }

    public void enablePrinter(String printerCode, @NotNull(message = "enable can not be null") Boolean enable) {
        PrinterEntity printerEntity = printerRepository.findByCode(printerCode);
        AssertUtil.assertNotNull(printerEntity, "no printer for printerCode");
        printerEntity.setEnable(enable);
        printerRepository.updateById(printerEntity);
    }

    public void remove(String printerCode) {
        printerRepository.removeByCode(printerCode);
    }

    /**
     * 打印机在线状态变更
     *
     * @param agentCode
     * @param printerName
     * @param isOnline
     */
    public void onlineStatusChange(String agentCode, String printerName, boolean isOnline) {
        PrinterEntity printerEntity = printerRepository.findByAgentCodeAndPrinterName(agentCode, printerName);
        if (Objects.isNull(printerEntity)) {
            log.error("no printer for agentCode: {}, printerName: {}", agentCode, printerName);
            return;
        }
        if (Objects.isNull(printerEntity.getIsOnline())) {
            printerEntity.setIsOnline(isOnline);
        } else {
            if (isOnline == printerEntity.getIsOnline()) {
                return;
            }
            printerEntity.setIsOnline(isOnline);
        }
        printerRepository.updateById(printerEntity);
    }

    public PrinterResDto find(String printerCode) {
        PrinterEntity printerEntity = printerRepository.findByCode(printerCode);
        return Objects.nonNull(printerEntity) ? PrinterResDto.fromDomain(printerEntity) : null;
    }
}
